import classNames from 'classnames';

import PropTypes from 'prop-types';
import React from 'react';
import ReactDOM from 'react-dom';

import { IDOMProps } from '../../utils/props';

import PopperJS, {
  Placement,
  ReferenceObject,
} from 'popper.js';

import { Fade } from '../fade';
import Portal from '../portal/Portal';

export interface IPopoverProps extends IDOMProps {
  className?: string;
  style?: React.CSSProperties;
  open: boolean;
  placement?: Placement;
  children: React.ReactNode;
  target?: HTMLElement | ReferenceObject;
  showArrow?: boolean;
  usePortal?: boolean;
  popoverRef?: (ref: HTMLDivElement | null) => void;
}

export interface IPopoverState {
  open: boolean;
  placement?: string;
}

export class Popover extends React.Component<IPopoverProps, IPopoverState> {
  public static propTypes = {
    className: PropTypes.string,
    style: PropTypes.object,
    /**
     *  是否展开popover，默认false
     */
    open: PropTypes.bool.isRequired,
    /**
     * popover定位位置，默认'bottom'
     */
    placement: PropTypes.oneOf([
      'auto', 'auto-start', 'auto-end',
      'bottom', 'bottom-start', 'bottom-end',
      'left', 'left-start', 'left-end',
      'right', 'right-start', 'right-end',
      'top', 'top-start', 'top-end',
    ]),
    /**
     * popper内容
     */
    children: PropTypes.node,
    /**
     * 用于定位popper的元素
     */
    target: PropTypes.element,
    /**
     * 是否显示箭头，默认true
     */
    showArrow: PropTypes.bool,
    /**
     * 是否使用Portal创建，默认true
     */
    usePortal: PropTypes.bool,
    popoverRef: PropTypes.func,
  };

  public static defaultProps = {
    style: {},
    placement: 'bottom',
    open: false,
    showArrow: true,
    usePortal: true,
  };

  public static getDerivedStateFromProps(nextProps: IPopoverProps) {
    if (nextProps.open || nextProps.placement) {
      return {
        placement: nextProps.placement,
        open: nextProps.open,
      };
    }
    return null;
  }

  /**
   * popper实例
   */
  public popper: null | PopperJS = null;

  /**
   * popper DOM节点
   */
  public popperRef: null | Element = null;

  constructor(props: IPopoverProps) {
    super(props);

    this.state = {
      open: props.open,
      placement: props.placement,
    };

    this.handleRef = this.handleRef.bind(this);
    this.handleOpen = this.handleOpen.bind(this);
    this.destroyPopper = this.destroyPopper.bind(this);
  }

  public componentDidUpdate(prevProps: IPopoverProps) {
    if (prevProps.open !== this.props.open && !this.props.open) {
      this.destroyPopper();
    }
    if (prevProps.open !== this.props.open ||
      prevProps.target !== this.props.target ||
      prevProps.placement !== this.props.placement) {
      this.handleOpen();
    }
  }

  /**
   * 组件移除后销毁this.popper
   */
  public componentWillUnMount() {
    this.destroyPopper();
  }

  public handleRef(node: HTMLDivElement) {
    this.popperRef = node;
    if (this.props.popoverRef) {
      this.props.popoverRef(node);
    }
  }

  /**
   * 显示popper
   */
  public handleOpen() {
    const { open, placement, target } = this.props;

    if (!this.popperRef || !target || !open) {
      return;
    }

    // 存在popper实例，先销毁
    this.destroyPopper();
    //  创建popper实例
    this.popper = new PopperJS(target, this.popperRef, {
      placement,
      modifiers: {
        flip: {
          enabled: true,
        },
        preventOverflow: {
          enabled: true,
          boundariesElement: 'scrollParent',
        },
      },
    });
  }

  /**
   * 销毁popper实例
   */
  public destroyPopper() {
    if (this.popper) {
      this.popper.destroy();
      this.popper = null;
    }
  }

  public render() {
    const { children, showArrow, className, style, usePortal, target, ...domProps } = this.props;
    const { open } = this.state;

    if (!open || !target) {
      return null;
    }

    const popover = (
      <div
        {...domProps as IDOMProps}
        className={classNames('br-popover', className)}
        style={{ ...style }}
        ref={this.handleRef}>
        <Fade active={open}>
          <div className="br-popover__content">
            {children}
          </div>
        </Fade>
        {showArrow && <div className="br-popper__arrow popper__arrow" x-arrow="true" />}
      </div>
    );

    if (usePortal) {
      return (
        <Portal onRendered={this.handleOpen.bind(this)}>
          {popover}
        </Portal>
      );
    }
    return popover;
  }
}
